#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>

#define PORT 8080
#define BUFFER_SIZE 1024

void *handle_client(void *arg);
void error_handling(char *message);

int main() {
	    int serv_sock, clnt_sock;
	        struct sockaddr_in serv_addr, clnt_addr;
		    socklen_t clnt_addr_size;
		        pthread_t thread_id;
			    
			    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
			        if (serv_sock == -1)
					        error_handling("socket() error");
				    
				    memset(&serv_addr, 0, sizeof(serv_addr));
				        serv_addr.sin_family = AF_INET;
					    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
					        serv_addr.sin_port = htons(PORT);
						    
						    if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
							            error_handling("bind() error");
						        
						        if (listen(serv_sock, 5) == -1)
								        error_handling("listen() error");
							    
							    clnt_addr_size = sizeof(clnt_addr);
							        
							        printf("Multi-threaded Knock Knock Server started on port %d\n", PORT);
								    
								    while (1) {
									            clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
										            if (clnt_sock == -1)
												                error_handling("accept() error");
											            
											            printf("New client connected, creating thread...\n");
												            
												            if (pthread_create(&thread_id, NULL, handle_client, (void*)&clnt_sock) != 0)
														                error_handling("pthread_create() error");
													            
													            pthread_detach(thread_id);
														        }
								        
								        close(serv_sock);
									    return 0;
}

void *handle_client(void *arg) {
	    int clnt_sock = *((int*)arg);
	        char buffer[BUFFER_SIZE];
		    pthread_t my_id = pthread_self();
		        
		        printf("[Thread %lu] Handling client\n", (unsigned long)my_id);
			    
			    strcpy(buffer, "Knock! Knock!\n");
			        write(clnt_sock, buffer, strlen(buffer));
				    
				    read(clnt_sock, buffer, BUFFER_SIZE-1);
				        printf("[Thread %lu] Client says: %s", (unsigned long)my_id, buffer);
					    
					    strcpy(buffer, "Who's there?\n");
					        write(clnt_sock, buffer, strlen(buffer));
						    
						    read(clnt_sock, buffer, BUFFER_SIZE-1);
						        printf("[Thread %lu] Client says: %s", (unsigned long)my_id, buffer);
							    
							    strcpy(buffer, "Haha, funny joke! Goodbye!\n");
							        write(clnt_sock, buffer, strlen(buffer));
								    
								    close(clnt_sock);
								        printf("[Thread %lu] Client disconnected\n", (unsigned long)my_id);
									    
									    return NULL;
}

void error_handling(char *message) {
	    perror(message);
	        exit(1);
}
